﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SmartMathLibrary.Blas.Engine;

namespace SmartMathLibrary.Blas
{
    /// <summary>
    /// This class provides an interface to the Blas level 1 routines.
    /// </summary>
    [Serializable]
    public static class ManagedBlasLevel1
    {
        /// <summary>
        /// Returns the sum of the absolute values of the elements of a vector. 
        /// </summary>
        /// <param name="n">The number of elements in the vectors x and y.</param>
        /// <param name="x">A reference to a one-dimensional array containing the elements of the vector x.</param>
        /// <param name="incx">The increment for the array x.</param>
        /// <returns>The sum of the absolute values of the elements of a vector.</returns>
        public static double Dasum(int n, double[] x, int incx)
        {
            unsafe
            {
                fixed (double* pX = x)
                {
                    return ManagedBlasProvider.dasum_(&n, pX, &incx);
                }
            }
        }

        /// <summary>
        /// Evaluates a vector plus the product of a scalar and a vector.
        /// </summary>
        /// <param name="n">The number of elements in the vectors x and y.</param>
        /// <param name="alpha">The scalar value used to multiply the elements of x.</param>
        /// <param name="x">A reference to a one-dimensional array containing the elements of the vector x.</param>
        /// <param name="incx">The increment for the array x.</param>
        /// <param name="y">A reference to a one-dimensional array containing the elements of the vector y. 
        /// The elements of y are overwritten with the result.</param>
        /// <param name="incy">The increment for the array y.</param>
        public static void Daxpy(int n, double alpha, double[] x, int incx, double[] y, int incy)
        {
            unsafe
            {
                fixed (double* pX = x)
                {
                    fixed (double* pY = y)
                    {
                        ManagedBlasProvider.daxpy_(&n, &alpha, pX, &incx, pY, &incy);
                    }
                }
            }
        }

        /// <summary>
        /// Copies a vector.
        /// </summary>
        /// <param name="n">The number of elements in the vectors x and y.</param>
        /// <param name="x">A reference to a one-dimensional array containing the elements of the vector x.</param>
        /// <param name="incx">The increment for the array x.</param>
        /// <param name="y">A reference to a one-dimensional array containing the elements of the vector y. 
        /// The elements of y are overwritten with the elements of x.</param>
        /// <param name="incy">The increment for the array y.</param>
        public static void Dcopy(int n, double[] x, int incx, double[] y, int incy)
        {
            unsafe
            {
                fixed (double* pX = x)
                {
                    fixed (double* pY = y)
                    {
                        ManagedBlasProvider.dcopy_(&n, pX, &incx, pY, &incy);
                    }
                }
            }
        }

        /// <summary>
        /// Computes the inner product of two vectors. 
        /// </summary>
        /// <param name="n">The number of elements in the vectors x and y.</param>
        /// <param name="x">A reference to a one-dimensional array containing the elements of the vector x.</param>
        /// <param name="incx">The increment for the array x.</param>
        /// <param name="y">A reference to a one-dimensional array containing the elements of the vector y. 
        /// The elements of y are overwritten with the result.</param>
        /// <param name="incy">The increment for the array y.</param>
        /// <returns>The dot product of the two vectors.</returns>
        public static double Ddot(int n, double[] x, int incx, double[] y, int incy)
        {
            unsafe
            {
                fixed (double* pX = x)
                {
                    fixed (double* pY = y)
                    {
                        return ManagedBlasProvider.ddot_(&n, pX, &incx, pY, &incy);
                    }
                }
            }
        }

        /// <summary>
        /// Computes the square root of sum of the squares of the elements of a vector. 
        /// </summary>
        /// <param name="n">The number of elements in the vector x.</param>
        /// <param name="x">A reference to a one-dimensional array containing the elements of the vector x.</param>
        /// <param name="incx">The increment for the array x.</param>
        /// <returns>The square root of sum of the squares of the elements of a vector.</returns>
        public static double Dnrm2(int n, double[] x, int incx)
        {
            unsafe
            {
                fixed (double* pX = x)
                {
                    return ManagedBlasProvider.dnrm2_(&n, pX, &incx);
                }
            }
        }

        /// <summary>
        /// Applies a Givens plane rotation.
        /// </summary>
        /// <param name="n">The number of elements in the vectors x and y.</param>
        /// <param name="x">A reference to a one-dimensional array containing the elements of the vector x.</param>
        /// <param name="incx">The increment for the array x.</param>
        /// <param name="y">A reference to a one-dimensional array containing the elements of the vector y.</param>
        /// <param name="incy">The increment for the array y.</param>
        /// <param name="c">The first rotation element, the cosine of the rotation angle.</param>
        /// <param name="s">The second rotation element, the sine of the rotation angle.</param>
        public static void Drot(int n, double[] x, int incx, double[] y, int incy, double c, double s)
        {
            unsafe
            {
                fixed (double* pX = x)
                {
                    fixed (double* pY = y)
                    {
                        ManagedBlasProvider.drot_(&n, pX, &incx, pY, &incy, &c, &s);
                    }
                }
            }
        }

        /// <summary>
        /// Generates the elements for a Givens plane rotation. 
        /// </summary>
        /// <param name="a">The first element of the input vector. On exit, this value is overwritten with 
        /// the rotated element r.</param>
        /// <param name="b">The second element of the input vector. On exit, this value is overwritten with 
        /// the reconstruction value z.</param>
        /// <param name="c">On exit, this value is overwritten with the first rotation element, the cosine of 
        /// the rotation angle.</param>
        /// <param name="s">On exit, this value is overwritten with the second rotation element, the sine of 
        /// the rotation angle.</param>
        public static void Drotg(ref double a, ref double b, ref double c, ref double s)
        {
            unsafe
            {
                fixed (double* pA = &a)
                {
                    fixed (double* pB = &b)
                    {
                        fixed (double* pC = &c)
                        {
                            fixed (double* pS = &s)
                            {
                                ManagedBlasProvider.drotg_(pA, pB, pC, pS);
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Applies a modified Givens rotation. 
        /// </summary>
        /// <param name="n">The number of elements in the vectors x and y.</param>
        /// <param name="x">A reference to a one-dimensional array containing the elements of the vector x.</param>
        /// <param name="incx">The increment for the array x.</param>
        /// <param name="y">A reference to a one-dimensional array containing the elements of the vector y.</param>
        /// <param name="incy">The increment for the array y.</param>
        /// <param name="param">An array defining the type of transform matrix H used.</param>
        public static void Drotm(int n, double[] x, int incx, double[] y, int incy, double[] param)
        {
            unsafe
            {
                fixed (double* pX = x)
                {
                    fixed (double* pY = y)
                    {
                        fixed (double* pParam = param)
                        {
                            ManagedBlasProvider.drotm_(&n, pX, &incx, pY, &incy, pParam);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Applies a modified Givens rotation. 
        /// </summary>
        /// <param name="d1">The first scale factor for the modified Givens transform.</param>
        /// <param name="d2">The second scale factor for the modified Givens transform.</param>
        /// <param name="x1">The first element of the input vector. On exit, this value is overwritten with 
        /// the rotated element.</param>
        /// <param name="y1">The second element of the input vector.</param>
        /// <param name="param">An array defining the type of transform matrix H used.</param>
        public static void Drotmg(ref double d1, ref double d2, ref double x1, double y1, double[] param)
        {
            unsafe
            {
                fixed (double* pD1 = &d1)
                {
                    fixed (double* pD2 = &d2)
                    {
                        fixed (double* pX1 = &x1)
                        {
                            fixed (double* pParam = param)
                            {
                                ManagedBlasProvider.drotmg_(pD1, pD2, pX1, &y1, pParam);
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Evaluates the product of a scalar and a vector. 
        /// </summary>
        /// <param name="n">The number of elements in the vector x.</param>
        /// <param name="alpha">The scalar value used to multiply the elements of x.</param>
        /// <param name="x">A reference to a one-dimensional array containing the elements of the vector x. The elements of x are overwritten with the result.</param>
        /// <param name="incx">The increment for the array x.</param>
        public static void Dscal(int n, double alpha, double[] x, int incx)
        {
            unsafe
            {
                fixed (double* pX = x)
                {
                    ManagedBlasProvider.dscal_(&n, &alpha, pX, &incx);
                }
            }
        }

        /// <summary>
        /// Exchanges the elements of two vectors. 
        /// </summary>
        /// <param name="n">The number of elements in the vectors x and y.</param>
        /// <param name="x">A reference to a one-dimensional array containing the elements of the vector x.</param>
        /// <param name="incx">The increment for the array x.</param>
        /// <param name="y">A reference to a one-dimensional array containing the elements of the vector y.</param>
        /// <param name="incy">The increment for the array y.</param>
        public static void Dswap(int n, double[] x, int incx, double[] y, int incy)
        {
            unsafe
            {
                fixed (double* pX = x)
                {
                    fixed (double* pY = y)
                    {
                        ManagedBlasProvider.dswap_(&n, pX, &incx, pY, &incy);
                    }
                }
            }
        }

        /// <summary>
        /// Finds out the index of the element of a vector with maximum absolute value. 
        /// </summary>
        /// <param name="n">The number of elements in the vector x.</param>
        /// <param name="x">A reference to a one-dimensional array containing the elements of the vector x.</param>
        /// <param name="incx">The incx.</param>
        /// <returns>The index of the element of a vector with maximum absolute value. </returns>
        public static int Idamax(int n, float[] x, int incx)
        {
            unsafe
            {
                fixed (float* pX = x)
                {
                    return ManagedBlasProvider.isamax_(&n, pX, &incx);
                }
            }
        }
    }
}